#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import time
import datetime
import consts
from consts import Errno

from aiokafka import AIOKafkaProducer
import aiomysql
import aioredis

from sanic import Sanic
from sanic.response import text, file
from sanic.response import json as sanic_json

app = Sanic("vehicle")
app.static('/static', './static')

HOST = '127.0.0.1'


def json_response(code=Errno.SUCCESS, data=None):
    if not data:
        data = {}
    return sanic_json(dict(code=code.value, msg=code.describe(), data=data))


@app.listener('before_server_start')
async def server_init(app, loop):
    app.ctx.producer = AIOKafkaProducer(
        loop=loop,
        value_serializer=lambda v: json.dumps(v).encode('utf-8'),
        bootstrap_servers=consts.KAFKA_HOST
    )
    await app.ctx.producer.start()


@app.listener('before_server_start')
async def setup_mysql(app, loop):
    app.ctx.db = await aiomysql.create_pool(
        host=HOST,
        port=3306,
        user='root',
        password='123456',
        db='django-vue-admin', loop=loop, charset='utf8', autocommit=True)


@app.listener('before_server_start')
async def setup_redis(app, loop):
    app.ctx.redis = aioredis.from_url(
        f"redis://{HOST}", encoding="utf-8", decode_responses=True
    )


@app.get("/index")
async def index(request):
    return await file("./tpl/index.html")


@app.post("/send_msg")
async def send_msg(request):
    data = request.json
    await app.ctx.redis.lpush(data['vin'], json.dumps(data))
    longtitude = data.get('longitude')
    latitude = data.get('latitude')
    if longtitude and latitude:
        await app.ctx.redis.set('location:%s' % data['vin'], f"{longtitude},{latitude}")
    # app.producer.sendconsts.VEHICLE_TOPIC, value=data['vin'])
    await app.ctx.producer.send('test', value=data, key=data['vin'].encode())
    return json_response()


@app.get("/send_vehicle_start/<vin>")
async def send_vehicle_start(request, vin):
    async with app.ctx.db.acquire() as conn:
        async with conn.cursor(aiomysql.DictCursor) as cur:
            await cur.execute('SELECT vin from vehicle_info WHERE vin=%s', (vin,))
            r = await cur.fetchall()
            if not r:
                return json_response(Errno.INVALID_VIN)

            # 更新状态为在线
            await cur.execute('UPDATE vehicle_info set vehicle_status=1 WHERE vin=%s', (vin,))

            # 更新行驶记录
            import uuid
            uid = uuid.uuid1()
            values = (uid, vin, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
            await cur.execute('INSERT into vehicle_move_info(id, vehicle_vin, start_time) VALUES (%s, %s, %s)', values)

    data = dict(uid=str(uid))
    return json_response(data=data)


@app.get("/send_vehicle_stop/<vin>")
async def send_vehicle_stop(request, vin):
    async with app.ctx.db.acquire() as conn:
        async with conn.cursor(aiomysql.DictCursor) as cur:
            await cur.execute('SELECT vin from vehicle_info WHERE vin=%s', (vin,))
            r = await cur.fetchall()
            if not r:
                return json_response(Errno.INVALID_VIN)

            # 更新状态为离线
            await cur.execute('UPDATE vehicle_info set vehicle_status=0 WHERE vin=%s', (vin,))

            # 更新行驶记录
            uid = request.args.get("uid")
            values = (uid, vin, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
            await cur.execute('UPDATE vehicle_move_info set end_time=%s WHERE id=%s',
                              (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), uid))
            # 行驶消息
            data = dict(
                vin=vin,
                uid=uid,
                vehicle_status=0,
                vtype="finish"
            )
            await app.ctx.producer.send('test', value=data, key=data['vin'].encode())

            # 清除队列
            await app.ctx.redis.delete(vin)

    return json_response()


@app.get("/send_vehicle_stop")
async def send_vehicle_stop(request):
    return text("send_vehicle_stop")


@app.middleware("request")
async def check_sign(request):
    if request.path not in ('/send_msg',):
        return None

    data = request.json

    app_device = {
        'app1': 'abcdef'
    }

    ts = int(data.get('timestamp', 0))
    app_key = data.get('app_key')
    sign = data.get('sign')

    # 判断是否包含timestamp，app_key，sign参数，如果不含有返回错误码
    if not (ts and app_key and app_device.get(app_key) and sign):
        return json_response(Errno.INVALID_AUTH_PARAMS)

    # 判断服务器接到请求的时间和参数中的时间戳是否在合里范围
    server_ts = int(time.time() * 1000)
    if (server_ts - ts) > consts.MAX_DIFF_TS:
        return json_response(Errno.INVALID_TIMESTAMP)

    # 根据用户请求的参数，服务器端按照同样的规则生成sign签名，对比签名看是否相等

    # 1.将所有POST参数，除去sign本身，按参数名字母升序排序
    params = dict()
    params.update(request.json)
    params.pop('sign')

    # 2.然后把排序后的参数按参数1值1参数2值2…参数n值n的方式拼接成一个字符串。
    params_list = sorted(params.items(), key=lambda k: k[0])
    params_str = [f'{p[0]}{p[1]}' for p in params_list]
    params_str = ''.join(params_str)

    # 3.把分配给接入方的验证密钥key拼接在第2步得到的字符串后面
    params_str = f'{params_str}{app_device.get(app_key)}'

    # 4.计算md5值
    import hashlib
    server_sign = hashlib.md5()
    server_sign.update(params_str.encode())
    server_sign = server_sign.hexdigest()

    # 5.比对sign是否相等
    if sign != server_sign:
        return json_response(Errno.INVALID_SIGN)
